// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rainbowchat.im.impl;

import io.netty.channel.Channel;

import java.util.Vector;
import java.util.concurrent.ConcurrentMap;

import net.x52im.mobileimsdk.server.protocal.Protocal;

import com.eva.epc.common.util.CommonUtils;
import com.eva.framework.dbpool.DBDepend;
import com.eva.framework.utils.LoggerFactory;
import com.x52im.rainbowchat.MyControllerJSON;
import com.x52im.rainbowchat.common.dto.cnst.OperateLog;
import com.x52im.rainbowchat.http.logic.LogicProcessor2;
import com.x52im.rainbowchat.http.logic.LogicUtils;
import com.x52im.rainbowchat.http.logic.dto.RosterElementEntity;
import com.x52im.rainbowchat.im.ChatServerEventListener;
import com.x52im.rainbowchat.im.ChatServerLauncher;
import com.x52im.rainbowchat.im.dto.CMDBody4AddFriendRequest;
import com.x52im.rainbowchat.im.dto.CMDBody4ProcessFriendRequest;
import com.x52im.rainbowchat.im.dto.MsgBody4Guest;
import com.x52im.rainbowchat.im.util.ChatServerUtils;
import com.x52im.rainbowchat.im.util.MessageHelper;
import com.x52im.rainbowchat.im.util.MessageHelper.SendResultObserver;
import com.x52im.rainbowchat.push.ios.APNSPushHelper;

/**
 * IM Server 服务器通用业务逻辑实现类（除群聊外的各种逻辑）.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 */
public class ChatLogicManager
{
	public ChatLogicManager()
	{
		//
	}
	
	// 处理临时聊天消息的最终投递
	public static boolean processMT42_OF_TEMP$CHAT$MSG_A$TO$SERVER(String fingerPrint, String dataContent) throws Exception
	{
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// 解析临时聊天消息
		final MsgBody4Guest tempChatMsgFromClient = MessageHelper.pareseTempChatMsg_A$TO$SERVER_Message(dataContent);
//		tempChatMsgFromClient.setMsgTime(""+System.currentTimeMillis());
		// 结果正常解析出来了
		if(tempChatMsgFromClient != null)
		{
			// 目标用户是否在线（准确地说是否在线于APP的IM服务端）
			String destUser_id = tempChatMsgFromClient.getT();
			final boolean isDestFriendOnline = liverUsersMgr.isOnline(destUser_id);
			
			// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
			SendResultObserver sendResultObserver = new SendResultObserver(){
				@Override
				public void update(boolean code, Protocal p) {
					// 返回值true：对于应用层逻辑来说，无论是在线发送成功，或者是跨服桥接成功，都算是发送成功
					if(code)
					{
						LoggerFactory.getLog().debug("[RBChat]服务器通知由uid="+tempChatMsgFromClient.getF()
								+"发起的临时聊天消息，已经成功发给在线好友uid="+tempChatMsgFromClient.getT()+".");
						
						// 说明：满足此条件，表示本地不在线但处于与Web互通模式的情况下，消息却发送成功，也需要进行App的push通知，
						//       因为前提是app端已不在线，进行push是合情合理的（否则桥接成功的情况下，app这边即使不在线也永远收
						//       不到app的push通知，除非web那端也要实现不在线时的app的push通知。
						// 备忘：此处的push逻辑是为了省去web那端不在线时的app push通知逻辑，日后支持全功能集群后，就不需
						//       要这个逻辑了，因为包括web集群实例在内，都将是对等的且必须支持app 的push逻辑——所以没必要以下代码。
						if(!isDestFriendOnline && ChatServerLauncher.bridgeEnabled)
						{
							// 尝试进行app端的push通知
							OfflineMessageManager.processOfflineMessage(p, true); // 注意此参数为true！
						}
					}
					// 返回值false：实时或桥接发送没有成功
					else
					{
						LoggerFactory.getLog().warn("[RBChat]服务器实时或桥接通知由uid="+tempChatMsgFromClient.getF()
								+"发起的临时聊天消息没有成功（目标uid="+tempChatMsgFromClient.getT()+"），马上考虑做离线处理！");
						
						// 将该条临时聊天消息插入到数据库中（以备下次接收方B上线后拿到）
						OfflineMessageManager.processOfflineMessage(p, false); ;
					}
				}
			};
			
			// 将临时聊天消息实时转发给本次消息接收者（如果用户连在本APPP实例则直发，否则开启与web互通时将桥接发）
			MessageHelper.sendTempChatMsg_SERVER$TO$B_Message(destUser_id, tempChatMsgFromClient, fingerPrint, sendResultObserver);
			
			/** 20190328 by Jack Jiang：按照目前的设计原则，为了减化架构和处理的复杂性，服务端主动发起
	          	的S2C类型消息或指令一律由发送者处理（详见设计备忘），切记！ */
			// ** 【用户消息收集】收集用户的聊天消息，以供后台进行用户行为分析，
			MessageRecordManager.colectChattingMsgAsync(dataContent, fingerPrint);
		}
		
		return true;
	}
	
	/**
	 * 处理发起好友请求指令的投递.
	 * 
	 * @param addFriendMetaFromClient 添加好友请求原数据（客户端A发过来的）
	 * @param liverUsersMgr
	 * @return
	 * @throws Exception
	 */
	public static boolean processMT05_OF_ADD_FRIEND_REQUEST_A$TO$SERVER(final CMDBody4AddFriendRequest addFriendMetaFromClient) throws Exception
	{
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// 结果正常解析出来了
		if(addFriendMetaFromClient != null)
		{
			// ** 合法性检查（同一个好友不能试图插2次到数据库中，虽然肯定插不进去）
			if(LogicProcessor2.db.queryData(
					"select 1 from missu_roster where user_uid="+addFriendMetaFromClient.getLocalUserUid()
						+" and friend_user_uid="+addFriendMetaFromClient.getFriendUserUid()).size()>0)
			{
				String responseForError = "Sorry, ID:"
						+addFriendMetaFromClient.getFriendUserUid()+" has been your lover, dosn't need add again.";
				// 把错误信息反馈给加好友请求者A
				MessageHelper.sendAddFriendRequestResponse_for$error_server$to$aMessage(addFriendMetaFromClient.getLocalUserUid(), responseForError, null);
				return false;
			}
			
			// ** 查询该人员的允许最大好友数、当前好友总数并判断是否超出最大好友数
			String[] friendCountQueryResult = LogicUtils.queryUserFriendCount(
					addFriendMetaFromClient.getLocalUserUid());
			if(friendCountQueryResult != null && friendCountQueryResult.length >=2)
			{
				int maxFriend = CommonUtils.getIntValue(friendCountQueryResult[0]);
				int curentFriend = CommonUtils.getIntValue(friendCountQueryResult[1]);
				LoggerFactory.getLog().debug("[RBChat]【发出】"+addFriendMetaFromClient.getLocalUserUid()
						+"：最大好友数="+maxFriend+"、当前总数="+curentFriend);
				
//				// 合法性检查（一个人只能有一个好友），在此作代码检查以便给用户友好提示，实际上db里有唯一索引（绝不会存一人多个好友）
//				if(LogicProcessor.db.queryData(
//						"select 1 from missu_roster where user_uid="+addFriendMetaFromClient.getLocalUserUid()).size() > 0)
				// 2014-01-09日（RBChat2.0以后版本）起：当当前好友总数>=最大允许可友总数时则不允许再加好友了
				if(curentFriend >= maxFriend)
				{
					String responseForError = "Sorry, you can only have "+maxFriend+" lovers, ID:"
							+addFriendMetaFromClient.getFriendUserUid()+" add Failed.";
					// 把错误信息反馈给加好友请求者A
					MessageHelper.sendAddFriendRequestResponse_for$error_server$to$aMessage( addFriendMetaFromClient.getLocalUserUid(), responseForError, null);
					return false;
				}
			}
			else
			{
				LoggerFactory.getLog().warn("[RBChat]查询"
						+addFriendMetaFromClient.getLocalUserUid()
						+"的最大好友数、当前总数结果有问题："
						+"friendCountQueryResult==null 或 friendCountQueryResult.length < 2 !");
			}
			
			// ** 将加好友请求插入到数据库表中（以备被请求者处理：同意或忽略）
			try
			{
				// 如果对该好友的加好友请求对方还没有处理，则只需要更新邀请信息的最新时间即可（不需要重复插数据库了）
				if(LogicProcessor2.db.queryData("select 1 from missu_friendreq " +
						"where user_uid="+addFriendMetaFromClient.getLocalUserUid()
							+" and be_user_uid="+addFriendMetaFromClient.getFriendUserUid()).size()>0)
				{
					LoggerFactory.getLog().debug("[RBChat]由uid="+addFriendMetaFromClient.getLocalUserUid()
							+"发起的加好友uid="+addFriendMetaFromClient.getFriendUserUid()
							+"的请求，已经存在，更新最近请求时间即可.");
					
					LogicProcessor2.db.update("update missu_friendreq set be_time="
							+DBDepend.getDefaultDatetimeFunc()+", be_desc=?, be_timestamp=?" +
							" where user_uid=? and be_user_uid=?"
							, new Object[]{addFriendMetaFromClient.getDesc()
											, ""+System.currentTimeMillis() // GMT+0的时间戳
											, addFriendMetaFromClient.getLocalUserUid()
											, addFriendMetaFromClient.getFriendUserUid()}, true);
				}
				// 否则是首次加该发友，插入一条邀请到数据库（此种情况是防止对方不在线，下次上线了还能来处理的情况）
				else
				{
					LogicProcessor2.db.update("insert into missu_friendreq(" +
						"user_uid,be_user_uid,be_desc,bt_ip,be_time,be_timestamp) " +
						"values(?,?,?,?,"+DBDepend.getDefaultDatetimeFunc()+",?)"
						, new Object[]{addFriendMetaFromClient.getLocalUserUid()
							, addFriendMetaFromClient.getFriendUserUid()
							, addFriendMetaFromClient.getDesc()
							, "[ip地址目前无法记录]"
							, ""+System.currentTimeMillis() // GMT+0的时间戳
						}, true);
				}
				
				// 目标用户是否在线
				String destUser_id = addFriendMetaFromClient.getFriendUserUid();
				final boolean isDestFriendOnline = liverUsersMgr.isOnline(destUser_id);

				RosterElementEntity ree = LogicUtils.queryUserInfoByUID(addFriendMetaFromClient.getLocalUserUid(), null);
				// 自20140213 RBChat2.2（含）以后ex1作为服务端转发加好友请求（给B）时存放验证说明文本使用（包括离线消息）
				ree.setEx1(addFriendMetaFromClient.getDesc());

				// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
				SendResultObserver sendResultObserver = new SendResultObserver(){
					@Override
					public void update(boolean code, Protocal p) {
						// 返回值true：对于应用层逻辑来说，无论是在线发送成功，或者是跨服桥接成功，都算是发送成功
						if(code)
						{
							LoggerFactory.getLog().debug("[RBChat]服务器通知由uid="+addFriendMetaFromClient.getLocalUserUid()
									+"发起的加好友请求，已经成功发给在线好友uid="+addFriendMetaFromClient.getFriendUserUid()+".");

							// 说明：满足此条件，表示本地不在线但处于与Web互通模式的情况下，消息却发送成功，也需要进行App的push通知，
							//       因为前提是app端已不在线，进行push是合情合理的（否则桥接成功的情况下，app这边即使不在线也永远收
							//       不到app的push通知，除非web那端也要实现不在线时的app的push通知。
							// 备忘：此处的push逻辑是为了省去web那端不在线时的app push通知逻辑，日后支持全功能集群后，就不需
							//       要这个逻辑了，因为包括web集群实例在内，都将是对等的且必须支持app 的push逻辑——所以没必要以下代码。
							if(!isDestFriendOnline && ChatServerLauncher.bridgeEnabled)
							{
								//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
								APNSPushHelper.tryPushAddFriendProcess_request(addFriendMetaFromClient.getFriendUserUid()
										, addFriendMetaFromClient.getLocalUserUid());
							}

//							return true;
						}
						else
						{
							LoggerFactory.getLog().warn("[RBChat]服务器实时或桥接通知由uid="+addFriendMetaFromClient.getLocalUserUid()
									+"发起的加好友请求没有成功（目标uid="+addFriendMetaFromClient.getFriendUserUid()+"），因离线和重试" +
									"机制的存在，本条提示可忽略。");

							//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
							APNSPushHelper.tryPushAddFriendProcess_request(addFriendMetaFromClient.getFriendUserUid()
									, addFriendMetaFromClient.getLocalUserUid());

//							return false;
						}
					}
				};
					
				// 发送加好友消息通知
				MessageHelper.sendAddFriendRequestInfo_server$to$bMessage(destUser_id, ree, sendResultObserver);
				return false; // 返回值当前暂时没有作用
			}
			catch (Exception e)
			{
				String responseForError = "Add lover (id:"+addFriendMetaFromClient.getFriendUserUid()
						+") failed, reason is "+e.getMessage();
				// 把错误信息反馈给加好友请求者A
				MessageHelper.sendAddFriendRequestResponse_for$error_server$to$aMessage(addFriendMetaFromClient.getLocalUserUid(), responseForError, null);
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * 处理好友请求(同意)指令的投递。
	 * 
	 * @param processFriendMetaFromClient 被添加者同意加好友请求原数据（客户端A发过来的）
	 * @param liverUsersMgr
	 * @return
	 * @throws Exception
	 */
	public static boolean processMT08_OF_PROCESS_ADD$FRIEND$REQ_B$TO$SERVER_AGREE(
			final CMDBody4ProcessFriendRequest processFriendMetaFromClient) throws Exception
	{
//		// 解析被添加者同意加好友请求原数据（客户端A发过来的）
//		CMDBody4ProcessFriendRequest processFriendMetaFromClient = 
//				MessageHelper.pareseProcessAdd$Friend$Req_B$To$Server_AGREEMessage(dataContent);
		
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// 结果正常解析出来了
		if(processFriendMetaFromClient != null)
		{
			// ** 查询该人员的允许最大好友数、当前好友总数并判断是否超出最大好友数
			String[] friendCountQueryResult = LogicUtils.queryUserFriendCount(
					processFriendMetaFromClient.getLocalUserUid());
			if(friendCountQueryResult != null && friendCountQueryResult.length >=2)
			{
				int maxFriend = CommonUtils.getIntValue(friendCountQueryResult[0]);
				int curentFriend = CommonUtils.getIntValue(friendCountQueryResult[1]);
				LoggerFactory.getLog().debug("[RBChat]【接收】"+processFriendMetaFromClient.getLocalUserUid()
						+"：最大好友数="+maxFriend+"、当前总数="+curentFriend);
				
				// 2014-01-09日（RBChat2.0以后版本）起：当当前好友总数>=最大允许可友总数时则不允许再加好友了
				if(curentFriend >= maxFriend)
				{
					String local_user_id = processFriendMetaFromClient.getLocalUserUid();
					// 如果本地用户在线的话，把错误信息发回给他
					if(!RosterElementEntity.DEFAULT_INVALID_UID_VALUE.equals(local_user_id))
					{
						String responseForError = "Sorry, you can only have "+maxFriend+" lovers, ID:"
								+processFriendMetaFromClient.getSrcUserUid()+" add Failed.";
						// 把错误信息反馈给加好友请求者A
						MessageHelper.sendAddFriendRequestResponse_for$error_server$to$aMessage(local_user_id, responseForError, null);
					}
					return false;
				}
			}
			
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 准备好更新数据的SQL语句
			Vector sqls = new Vector(2);
			Vector values = new Vector(2);
			// 不存在就插入，否则忽略
			if(LogicProcessor2.db.queryData("select 1 from missu_roster where user_uid="+
					processFriendMetaFromClient.getLocalUserUid()+" and friend_user_uid="
					+processFriendMetaFromClient.getSrcUserUid()).size()<=0)
			{
				// 插入B的好友记录（好友是A）
				sqls.add("INSERT INTO missu_roster(user_uid,friend_user_uid,add_time) " +
						"VALUES(?,?,"+DBDepend.getDefaultDatetimeFunc()+")");
				values.add(new Object[]{processFriendMetaFromClient.getLocalUserUid()
						,processFriendMetaFromClient.getSrcUserUid()});
			}
			
			// 不存在就插入，否则忽略
			if(LogicProcessor2.db.queryData("select 1 from missu_roster where user_uid="+
					processFriendMetaFromClient.getSrcUserUid()+" and friend_user_uid="+processFriendMetaFromClient.getLocalUserUid()).size()<=0)
			{
				// 插入A的好友记录（好友是B）
				sqls.add("INSERT INTO missu_roster(user_uid,friend_user_uid,add_time) " +
						"VALUES(?,?,"+DBDepend.getDefaultDatetimeFunc()+")");
				values.add(new Object[]{processFriendMetaFromClient.getSrcUserUid()
						,processFriendMetaFromClient.getLocalUserUid()});
			}
			
			// 存在就删除
			if(LogicProcessor2.db.queryData("select 1 from missu_friendreq where user_uid="+
					processFriendMetaFromClient.getSrcUserUid()+" and be_user_uid="+processFriendMetaFromClient.getLocalUserUid()).size()>0)
			{
				// 删除A发过来（给B）的加好友请求信息
				sqls.add("DELETE FROM missu_friendreq where user_uid=? and be_user_uid=?");
				values.add(new Object[]{processFriendMetaFromClient.getSrcUserUid()
						,processFriendMetaFromClient.getLocalUserUid()});
			}
			
			// 存在就删除
			if(LogicProcessor2.db.queryData("select 1 from missu_friendreq where user_uid="+
					processFriendMetaFromClient.getLocalUserUid()+" and be_user_uid="+processFriendMetaFromClient.getSrcUserUid()).size()>0)
			{
				// 删除B发给A的加好友请求信息（有可能会有这种情况，反正他们肯定成为好友了）
				// B发给A的好友请求，可能是曾今B也主动向A发过好友请求
				sqls.add("DELETE FROM missu_friendreq where user_uid=? and be_user_uid=?");
				values.add(new Object[]{processFriendMetaFromClient.getLocalUserUid()
						,processFriendMetaFromClient.getSrcUserUid()});
			}
			
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 提交到数据库中
			boolean sucess = false;
			String responseForError = null;
			try
			{
				LogicProcessor2.db.update(sqls, values, true);
				sucess = true;
			}
			catch (Exception e)
			{
				responseForError = "Prompt: Accept id:"
						+processFriendMetaFromClient.getSrcUserUid()+"'s request for add lover failed, reaseon is "+e.getMessage();
			}
			
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 处理成功， 通知双
			// 		方客户端（如果在线的话，不在线当然就不需要通知了，下次登陆即可载入好友列表）
			String localUser_id = null;//RosterElementEntity.DEFAULT_INVALID_UID_VALUE;
			if(sucess)
			{
				//** 尝试取出B用户和原A用户的在线user_id =======================================================
				final String srcUser_id = processFriendMetaFromClient.getSrcUserUid();
				localUser_id = processFriendMetaFromClient.getLocalUserUid();
				
				// A用户是否在线
				final boolean srcUserOnline = liverUsersMgr.isOnline(srcUser_id);
				// B用户是否在线
				boolean localUserOnline = liverUsersMgr.isOnline(localUser_id);
				
				// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
				SendResultObserver sendResultObserverA = new SendResultObserver(){
					@Override
					public void update(boolean code, Protocal p) {
						// 返回值true：对于应用层逻辑来说，无论是在线发送成功，或者是跨服桥接成功，都算是发送成功
						if(code)
						{
							// 说明：满足此条件，表示本地不在线但处于与Web互通模式的情况下，消息却发送成功，也需要进行App的push通知，
							//       因为前提是app端已不在线，进行push是合情合理的（否则桥接成功的情况下，app这边即使不在线也永远收
							//       不到app的push通知，除非web那端也要实现不在线时的app的push通知。
							// 备忘：此处的push逻辑是为了省去web那端不在线时的app push通知逻辑，日后支持全功能集群后，就不需
							//       要这个逻辑了，因为包括web集群实例在内，都将是对等的且必须支持app 的push逻辑——所以没必要以下代码。
							if(!srcUserOnline && ChatServerLauncher.bridgeEnabled)
							{
								//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
								APNSPushHelper.tryPushAddFriendProcess_accept(srcUser_id, processFriendMetaFromClient.getLocalUserNickName());
							}
						}
						// 尝试离线通知好友请求发起者：他的加好友请求被同意了！
						else
						{
							//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
							APNSPushHelper.tryPushAddFriendProcess_accept(srcUser_id, processFriendMetaFromClient.getLocalUserNickName());
						}
					}
				};
				// 将好友添加成功的消息尝试实时发送给A用户（原加好友的发起人），不在线则会则在
				// 下次上线重新加载好友列表，所以不需要像普通消息一样的离线处理机制
				// -> 将B用户的个人信息发给A：
				MessageHelper.sendProcessAdd$Friend$Req_friend$Info$Server$To$ClientMessage(srcUser_id
						, LogicUtils.queryUserInfoByUID(processFriendMetaFromClient.getLocalUserUid(), null).setLiveStatus(localUserOnline?1:0), sendResultObserverA);
				
				// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
				SendResultObserver sendResultObserver = new SendResultObserver(){
					@Override
					public void update(boolean code, Protocal p) {
						if(code){
							// 什么也不需要做！
						}
						else{
							// 好友请求的处理者就是被加者，因而不存在也不需要通过App的Push来即时通知本次请求的处理者（否则体验也太怪了）！
						}
					}
				};
				// 将好友添加成功的消息尝试实时发送给B用户（本次处理好友请求的发起方）
				// -> 将A用户的信人信息发给B（也就是发给本次好友请求的处理者）：
				MessageHelper.sendProcessAdd$Friend$Req_friend$Info$Server$To$ClientMessage(localUser_id
						, LogicUtils.queryUserInfoByUID(processFriendMetaFromClient.getSrcUserUid(), null).setLiveStatus(srcUserOnline?1:0), sendResultObserver);
				
			}
			//** 尝试将出错信息发回给处理加好友请求的B
			else
			{
				// B用户在线（本次处理好友请求的发起方）
				if(liverUsersMgr.isOnline(localUser_id))
				{
					// 将出错信息及时反馈给B（本次处理的发起者）
					MessageHelper.sendProcessAddFriendReq_response$For$Rrror_server$to$bMessage(localUser_id, responseForError, null);
				}
			}
		}
		
		return true;
	}
	
	/**
	 * 处理好友请求(拒绝)指令的投递。
	 * 
	 * @param processFriendMetaFromClient 被添加者拒绝加好友请求原数据（客户端A发过来的）
	 * @param liverUsersMgr
	 * @throws Exception
	 */
	public static void processMT09_OF_PROCESS_ADD$FRIEND$REQ_B$TO$SERVER_REJECT(
			final CMDBody4ProcessFriendRequest processFriendMetaFromClient) throws Exception
	{
//		// 解析被添加者拒绝加好友请求原数据（客户端A发过来的）
//		CMDBody4ProcessFriendRequest processFriendMetaFromClient = 
//				MessageHelper.pareseProcessAdd$Friend$Req_B$To$Server_REJECTMessage(dataContent);
		
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// 结果正常解析出来了
		if(processFriendMetaFromClient != null)
		{
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 准备好更新数据的SQL语句
			Vector sqls = new Vector(2);
			Vector values = new Vector(2);
			
			// 存在就删除
			if(LogicProcessor2.db.queryData("select 1 from missu_friendreq where user_uid="+
					processFriendMetaFromClient.getSrcUserUid()+" and be_user_uid="+processFriendMetaFromClient.getLocalUserUid()).size()>0)
			{
				// 删除A发过来（给B）的加好友请求信息（B如果发给A的就不用像“同意”里
				// 的处理一样——同时删除了只管删除A曾发过来的请求即可）
				sqls.add("DELETE FROM missu_friendreq where user_uid=? and be_user_uid=?");
				values.add(new Object[]{processFriendMetaFromClient.getSrcUserUid()
						,processFriendMetaFromClient.getLocalUserUid()});
			}
			
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 提交到数据库中
			boolean sucess = false;
			String responseForError = null;
			try
			{
				LogicProcessor2.db.update(sqls, values, true);
				sucess = true;
			}
			catch (Exception e)
			{
				responseForError = "Prompt: Reject id:"
						+processFriendMetaFromClient.getSrcUserUid()+" for add lover failed, reason is "+e.getMessage();
			}
			
			//**××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 处理成功， 通知被拒
			// 绝者的客户端（如果在线的话，不在线当然就不需要通知了，下次登陆即可载入数据库中存放的提示信息即可）
			String localUser_id = RosterElementEntity.DEFAULT_INVALID_UID_VALUE;
			if(sucess)
			{
				//** 尝试取出B用户和原A用户的在线user_id =======================================================
				String srcUser_id = processFriendMetaFromClient.getSrcUserUid();
				localUser_id = processFriendMetaFromClient.getLocalUserUid();
				
				// A用户是否在线
				final boolean srcUserOnline = liverUsersMgr.isOnline(srcUser_id);
				
				// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
				SendResultObserver sendResultObserver = new SendResultObserver(){
					@Override
					public void update(boolean code, Protocal p) {
						// 返回值true：对于应用层逻辑来说，无论是在线发送成功，或者是跨服桥接成功，都算是发送成功
						if(code)
						{
							// 说明：满足此条件，表示本地不在线但处于与Web互通模式的情况下，消息却发送成功，也需要进行App的push通知，
							//       因为前提是app端已不在线，进行push是合情合理的（否则桥接成功的情况下，app这边即使不在线也永远收
							//       不到app的push通知，除非web那端也要实现不在线时的app的push通知。
							// 备忘：此处的push逻辑是为了省去web那端不在线时的app push通知逻辑，日后支持全功能集群后，就不需
							//       要这个逻辑了，因为包括web集群实例在内，都将是对等的且必须支持app 的push逻辑——所以没必要以下代码。
							if(!srcUserOnline && ChatServerLauncher.bridgeEnabled)
							{
								//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
								APNSPushHelper.tryPushAddFriendProcess_reject(processFriendMetaFromClient.getLocalUserNickName()
										, processFriendMetaFromClient.getSrcUserUid());
							}
						}
						else
						{
							//** 备忘：目前拒绝好友请求没有做离线处理，因为从产品的角度来说，“拒绝”是种不好的体验，
							//**       目前可以采取静默处理（也就是不离线处理），一是技术上也简单，二是用户体验上
							//**       也更人性化，必竟对于被拒者来说，没有看到通过，那就有可能被拒或对方没看到，
							//**       从使用心理上来说，如果真的想要加对方，大不了再加一次或再加几次呗！
							
//							// 将B的拒绝信息存到数据库里，以便下次A登陆时，能看到提示消息，从而知
//							// 道他的加好友（加的B）的请求已经被拒绝了！
//							LogicProcessor2.db.update("insert into missu_other_alarms(" +
//									"user_uid,alarm_type,alarm_title,alarm_msg_content,alarm_time) values(?,?,?,?,?)"
//									, new Object[]{processFriendMetaFromClient.getSrcUserUid(), "2"
//										, "Add lover denied"
//										, "Request add lover "+processFriendMetaFromClient.getLocalUserNickName()+" has been reject."
//										, new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())}, true);
							
							//## 尝试向ios设备进行APNs离线消息推送（推送是基于netty+HTTP/2实现，因而是异步的不会阻塞余下代码执行）
							APNSPushHelper.tryPushAddFriendProcess_reject(processFriendMetaFromClient.getLocalUserNickName()
									, processFriendMetaFromClient.getSrcUserUid());
						}
					}
				};
				
				// A用户在线（原加好友的发起人）
				// --> 将拒绝者的个人信息发给A（以便及时告之他，加好友被拒了！）：
				MessageHelper.sendProcessAdd$Friend$Req_SERVER$TO$A_REJECT_RESULTMessage(srcUser_id
						, LogicUtils.queryUserInfoByUID(processFriendMetaFromClient.getLocalUserUid(), null), sendResultObserver);
			}
			//** 尝试将出错信息发回给处理加好友请求的B
			else
			{
				// B用户在线（本次处理好友请求的发起方）
				if(!RosterElementEntity.DEFAULT_INVALID_UID_VALUE.equals(localUser_id))
				{
					// 将出错信息及时反馈给B（本次处理的发起者）
					MessageHelper.sendProcessAddFriendReq_response$For$Rrror_server$to$bMessage(localUser_id, responseForError, null);
				}
			}
		}
	}
	
	public static void processUserLoginAction(final String userId, String extra, Channel session)
	{
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// 将该用户加入到在线用户列表
		liverUsersMgr.userOnline(userId);
		// TODO 用于debug时，生产时可以去掉哦！
		liverUsersMgr.printLiveUsers();

		// ** 用户登陆成功后由服务端通知他的好友，他上线了，此时就把它的user_id传到服务端了！
		// 在线用户列表
		ConcurrentMap<String, Channel> liveUsers = 
				ChatServerEventListener.getInstance().getLiverUsersMgr().getLiveUsers();
		try
		{
			// 该用户的好友列表
			Vector<Vector> roster = ChatServerUtils.getRosterByUid(userId);

			// 遍历好友列表，看看它有哪些好友是在线的
			for(Vector friendRow : roster)
			{
				// 好友uid
				final String friend_user_uid = (String)friendRow.get(0);
				final String friend_user_mail = (String)friendRow.get(1);
//				// 自已的uid（顺便取出来备用）
//				String user_uid = (String)friendRow.get(2);
				
				// FIXME: 备忘-> 因无法目前无全功能集群支持，无法取得所有im接入层的在线列表，所以
				//             当开通与web端互通时，就无法将上、下线通知给跟机器端了。不过，在线状态
				//             这种很难维护的东西，可以考虑像微信一样，在以后的版本中逐渐去掉，实际上来说
				//             ，用处也不是很大，只是传统IM（比如QQ）时代遗留下来的习惯而已！
				
				// 遍历在线好友列表，看看该好友是否在线
				if(liveUsers.containsKey(friend_user_uid))
				{
					// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
					SendResultObserver sendResultObserver = new SendResultObserver(){
						@Override
						public void update(boolean code, Protocal p) {
							if(code)
								LoggerFactory.getLog().debug("[RBChat]》登陆用户"+userId+"向它的好友"
										+friend_user_uid+"/"+friend_user_mail+"发送上线通知成功！");
							else
								LoggerFactory.getLog().debug("[RBChat]》登陆用户"+userId+"向它的好友"
										+friend_user_uid+"/"+friend_user_mail+"发送上线通知失败，错误code="+code);
						}
					};
					MessageHelper.sendOnlineMessage(friend_user_uid, userId, sendResultObserver);
				}
				else
				{
					LoggerFactory.getLog().debug("[RBChat]》登陆用户"+userId+"的好友"
							+friend_user_uid+"/"+friend_user_mail+"不在线，无需发送上线通知。");
				}
			}
		}
		catch (Exception e)
		{
			LoggerFactory.getLog().warn(e.getMessage(), e);
		}
	}

	public static void processUserLogoutAction(String userId, Channel session, int beKickoutCode)
	{
		// 在线用户列表
		UsersStatusManager liverUsersMgr = ChatServerEventListener.getInstance().getLiverUsersMgr();
		
		// ** 用户注销后由服务端通知他的好友，他下线了！
		// 下线的用户，必然之前已经在在线列表里了，因为通过它的user_id就能查到它登陆时的uid或者mail了
		if(userId != null)
		{
			try
			{
				// 该用户的好友列表
				Vector<Vector> roster = ChatServerUtils.getRosterByUid(userId);

				// 遍历好友列表，看看它有哪些好友是在线的
				for(Vector friendRow : roster)
				{
					// 好友uid
					final String friend_user_uid = (String)friendRow.get(0);
					final String friend_user_mail = (String)friendRow.get(1);
					// 自已的uid（顺便取出来备用）
					final String user_uid = (String)friendRow.get(2);
					
					// FIXME: 备忘-> 因无法目前无全功能集群支持，无法取得所有im接入层的在线列表，所以
					//             当开通与web端互通时，就无法将上、下线通知给跟机器端了。不过，在线状态
					//             这种很难维护的东西，可以考虑像微信一样，在以后的版本中逐渐去掉，实际上来说
					//             ，用处也不是很大，只是传统IM（比如QQ）时代遗留下来的习惯而已！

					// 看看该好友是否在线
//					if(liveUsers.containsKey(friend_user_uid))
					if(liverUsersMgr.isOnline(friend_user_uid))
					{
						// 指令发送结果观察者（因为通信底层netty是异步通知数据发送结果的）
						SendResultObserver sendResultObserver = new SendResultObserver(){
							@Override
							public void update(boolean code, Protocal p) {
								if(code)
									LoggerFactory.getLog().info("[RBChat]《登陆用户"+user_uid+"向它的好友"
											+friend_user_uid+"/"+friend_user_mail+"发送下线通知成功！");
								else
									LoggerFactory.getLog().warn("[RBChat]《登陆用户"+user_uid+"向它的好友"
											+friend_user_uid+"/"+friend_user_mail+"发送下线通知失败，错误code="+code);
							}
						};
						MessageHelper.sendOfflineMessage(friend_user_uid, user_uid, sendResultObserver);
					}
					else
					{
						LoggerFactory.getLog().debug("[RBChat]《登陆用户"+userId+"的好友"
								+friend_user_uid+"/"+friend_user_mail+"不在线，无需发送下线通知【END】。");
					}
				}
			}
			catch (Exception e)
			{
				LoggerFactory.getLog().warn(e.getMessage(), e);
			}
		}
		else
		{
			LoggerFactory.getLog().error("[RBChat]当前正在离线的用户"+userId+"为null？？？");
		}

		// 将该用户从在线用户列表中去掉
		liverUsersMgr.userOffline(userId);

		//增加退出系统日志
		OperateLog oprLog = new OperateLog(OperateLog.OPERATE_LOG_TYPE_LOGOUT_IM_SERVER
				, userId
				, "");
		try
		{
			MyControllerJSON.addOperateLog(oprLog);
		}
		catch (Exception e)
		{
			LoggerFactory.getLog().warn(e.getMessage(), e);
		}
		
		// TODO 用于debug时，生产时可以去掉哦！
		liverUsersMgr.printLiveUsers();
	}
}
